/**
 * 
 */
package smallChanges.statements;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import smallChanges.Tokenization.CodeRange2D;

/**
 * @author jspacco
 *
 */
public class ClassDeclaration extends CodeRange2D implements Iterable<Statement>
{
    private StatementHeader header;
    private List<MethodDeclarationStatement> methodList=
        new LinkedList<MethodDeclarationStatement>();
    // fields and static initializer
    private List<FieldDeclarationStatement> fieldList=
        new LinkedList<FieldDeclarationStatement>();
    private StatementList staticInitializer=StatementList.EMPTY_STATEMENT_LIST;
    private List<ClassDeclaration> innerClassList=
        new LinkedList<ClassDeclaration>();
    private String name;
    private ClassDeclaration parent=null;
    
    private transient int numInnerClasses=0;
    private transient int numMethodScopedInnerClasses=0;
    
    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List<Statement> flatten() {
        List<Statement> result=new LinkedList<Statement>();
        result.add(header);
        if( staticInitializer != null)
        {
        	result.addAll(staticInitializer.flatten());
        }
        result.addAll(fieldList);
        for (MethodDeclarationStatement m : methodList) {
            result.addAll(m.flatten());
        }
        for (ClassDeclaration cd : innerClassList) {
            result.addAll(cd.flatten());
        }
        return result;
    }
    
    /**
     * Returns an iterator that will return each statement in this class.
     * Note that this method returns every statement in the class exactly once,
     * but <b>does not</b> respect the original ordering of the statements.
     * 
     * @see java.lang.Iterable#iterator()
     */
    public Iterator<Statement> iterator() {
        return flatten().iterator();
    }
    
    public static List<Statement> flatten(List<ClassDeclaration> list)
    {
        List<Statement> result=new LinkedList<Statement>();
        for (ClassDeclaration cd : list) {
            result.addAll(cd.flatten());
        }
        return result;
    }
    
    public void addMethodDeclarationStatement(MethodDeclarationStatement m) {
        if (methodList==null) {
            methodList=new LinkedList<MethodDeclarationStatement>();
        }
        methodList.add(m);
    }
    
    public void addClassDeclaration(ClassDeclaration c) {
        if (innerClassList==null) {
            innerClassList=new LinkedList<ClassDeclaration>();
        }
        c.setParent(this);
        innerClassList.add(c);
    }
    
    public StatementHeader getHeader()
    {
        return header;
    }
    public void setHeader(StatementHeader header)
    {
        this.header = header;
        // whenever someone sets the header, we can use the beginning of
        // the header to set the beginning of the whole class
        setBeginLine(header.getBeginLine());
        setBeginColumn(header.getBeginColumn());
    }
    
    public List<MethodDeclarationStatement> getMethodList()
    {
        return methodList;
    }
    public void setMethodList(List<MethodDeclarationStatement> methodList)
    {
        this.methodList = methodList;
    }
    public StatementList getStaticInitializer()
    {
        return staticInitializer;
    }
    public void setStaticInitializer(StatementList staticInitializer)
    {
        this.staticInitializer = staticInitializer;
    }

    public void addFieldDeclaration(FieldDeclarationStatement fds)
    {
       fieldList.add(fds); 
    }

    public List<FieldDeclarationStatement> getFieldList()
    {
        return fieldList;
    }

    public List<ClassDeclaration> getInnerClasses()
    {
        return innerClassList;
    }
    public ClassDeclaration getParent()
    {
        return parent;
    }

    public void setParent(ClassDeclaration parent)
    {
        this.parent = parent;
    }

    public String getNextAnonymousInnerClassName()
    {
        return getName()+"$"+(++numInnerClasses);
    }
    
    public String getNextMethodScopedInnerClassName(String name)
    {
        return getName()+"$"+(++numMethodScopedInnerClasses)+name;
    }
}
